package com.gdteam.kernel.content.impl;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;

import com.gdteam.kernel.content.ContentEnvironment;
import com.gdteam.kernel.content.ContentIndexationService;
import com.gdteam.kernel.content.FileDocument;
import com.gdteam.kernel.main.ServiceManager;


public class DefaultContentIndexationServiceImpl extends TimerTask implements ContentIndexationService, FilenameFilter {
    
    private static final Pattern IMAGE_PATTERN = Pattern.compile("(jpg|jpeg|gif|png|bmp)", Pattern.CASE_INSENSITIVE);
    private static final Pattern MUSIC_PATTERN = Pattern.compile("(mp3|wave|wav|aiff|ogg)", Pattern.CASE_INSENSITIVE);
    private static final Pattern MOVIE_PATTERN = Pattern.compile("(mp4|mpeg|mpeg4|avi|mov)", Pattern.CASE_INSENSITIVE);
    private static final Pattern DOC_PATTERN = Pattern.compile("(doc|docx|xls|xlsx|pdf)", Pattern.CASE_INSENSITIVE);
    
    private Collection<String> includedpaths = new HashSet<String>();
    private Collection<String> excludespaths = new HashSet<String>();
    
    private IndexWriter idxwriter = null;
    private Analyzer analyzer = null;
    
    private Logger log = null;
    
    private File root = null;
    private File idxFolder = null;
    
    private boolean started = false;
    private boolean running = false;
    
    private Timer timer = null;

    public DefaultContentIndexationServiceImpl(ServiceManager manager,String rootdir, Logger log, String homedir) {
        this.log = log;
        try {
            this.analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
            this.root = new File(rootdir);
            
            if (!root.exists() || !root.canRead() || !root.isDirectory()) {
                throw new IllegalArgumentException("Invalid root directory for index");
            } else {
                this.includedpaths.add(root.getAbsolutePath());
            }
            
            idxFolder = new File(homedir,ContentEnvironment.IDX_FOLDER);
            idxFolder.mkdirs();
            
        } catch (Exception e) {
            log.error("Could not create index writer {}", e);
        }
    }
    
    public ContentIndexationService addIndexablePaths(String... paths) {
        return this;
    }

    public void start() {
        if (!started) {
            this.timer = new Timer(getName() +"-");
            this.timer.schedule(this, 0, 5*60*1000);
            this.started = true;
        }
    }

    public void stop() {
        if (started) {
            this.timer.cancel();
            this.timer.purge();
            this.timer = null;
            this.started = false;
        }
    }

    public String getName() {
        return ContentIndexationService.FQN;
    }
    
    public boolean accept(File dir, String name) {
        boolean res = true;
        
        res &= !name.startsWith(".");
        
        int idx = name.lastIndexOf(".");
        
        if (-1 != idx && res) {
            String ext = name.substring(idx+1);
            boolean extension = false;
            extension |= IMAGE_PATTERN.matcher(ext).matches();
            extension |= MOVIE_PATTERN.matcher(ext).matches();
            extension |= MUSIC_PATTERN.matcher(ext).matches();
            extension |= DOC_PATTERN.matcher(ext).matches();
            
            res &= extension;
        }
        
        return res;
    }
        
    private void doIndex(IndexWriter writer, File file) {
        // lets scan and add document to index
        if (file.canRead()) {
            if (file.isDirectory()) {
                for (File child: file.listFiles(this)) {
                    doIndex(writer,child);
                }
            } else {
                try {
                    Document doc = FileDocument.document(file);
                    writer.addDocument(doc);
                } catch (IOException ioe) {
                    log.error("Trouble during file {} indexation, {}", file.getAbsolutePath(),ioe);
                }
            }
        }
        
    }

    @Override
    public void run() {
        
        if (!running) {
            running = true;
            try {
                this.idxFolder.mkdirs();
                Directory d = new SimpleFSDirectory(this.idxFolder);
                
                this.idxwriter = new IndexWriter(d,this.analyzer,true,MaxFieldLength.UNLIMITED);
                for (String path: this.includedpaths) {
                    doIndex(this.idxwriter,new File(path));
                }
                
                this.idxwriter.optimize();
                this.idxwriter.close();
                
                log.info("Indexation over");
                
            } catch (LockObtainFailedException le) {
                log.warn("Could not get write lock => index deletion planned on exit");
                this.idxFolder.deleteOnExit();
                
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                running = false;
            }
        }
        
    }

    public boolean isStarted() {
        return this.started;
    }

    public String getIndexPath() {
        return this.idxFolder.getAbsolutePath();
    }

}
